﻿using System;
using System.Collections.Generic;
using System.Text;
using DO.Clustering;

namespace BLL.Relations.VectorDistances
{
    /// <summary>
    /// 
    /// </summary>
    public class WeightedFeatureSimilarityCalculator:IDistanceCalculator 
    {
        #region IDistanceCalculator Members

        public double CalculateDistance(
            Dictionary<int, FeatureFreq> FeatureFreqs1,
            Dictionary<int, FeatureFreq> FeatureFreqs2, 
            ref Dictionary<int,int> contributingFeatures)
        {
            if (FeatureFreqs1 == null || FeatureFreqs1.Count == 0 ||
                FeatureFreqs2 == null || FeatureFreqs2.Count == 0)
                return 0;

            double score = 0;
            double perfectScore = 0;
            foreach (int FeatureID in FeatureFreqs1.Keys)
            {
                if (FeatureFreqs2.ContainsKey(FeatureID))
                {
                    score +=
                        (FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count +
                        FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count);
                    contributingFeatures.Add(
                        FeatureID,
                        Math.Min(FeatureFreqs1[FeatureID].Count, FeatureFreqs2[FeatureID].Count));
                }

                perfectScore += FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count;
            }
            foreach (int FeatureID in FeatureFreqs2.Keys)
            {
                perfectScore += FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count;
            }
            return (double)(perfectScore - score) / perfectScore;
        }

        public double CalculateDistance(
            Dictionary<int, FeatureFreq> FeatureFreqs1,
            Dictionary<int, FeatureFreq> FeatureFreqs2, 
            Dictionary<int, double> FeatureWeights,
            bool useNGramSimilarity, int ngramLen,
            ref Dictionary<int, int> contributingFeatures)
        {
            if (FeatureFreqs1 == null || FeatureFreqs1.Count == 0 ||
                FeatureFreqs2 == null || FeatureFreqs2.Count == 0)
                return 0;

            double score = 0;
            double perfectScore = 0;
            Dictionary<int, Dictionary<int, double>> featureSimilarityMatrix =
               FeatureSimilarityMatrix.BuildFeatureDistanceMatrixUsingNgram(
                   FeatureFreqs1, FeatureFreqs2);

            foreach (int FeatureID in FeatureFreqs1.Keys)
            {
                double FeatureScore = 0;
                if (FeatureFreqs2.ContainsKey(FeatureID))
                {
                    FeatureScore =
                        (FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count +
                        FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count);
                    
                    contributingFeatures.Add(
                        FeatureID,
                        Math.Min(FeatureFreqs1[FeatureID].Count, FeatureFreqs2[FeatureID].Count));
                }
                else if(useNGramSimilarity && ngramLen>0)
                {
                    Dictionary<int, double> similarFeatures = featureSimilarityMatrix[FeatureID];
                    foreach (int similarFeatureID in similarFeatures.Keys)
                    {
                        if (FeatureFreqs2.ContainsKey(similarFeatureID) &&
                            FeatureFreqs2[similarFeatureID].Count * FeatureFreqs2[similarFeatureID].Weight > FeatureScore)
                            FeatureScore = (FeatureFreqs1[FeatureID].Weight*FeatureFreqs1[FeatureID].Count +
                                         FeatureFreqs2[similarFeatureID].Count*FeatureFreqs2[similarFeatureID].Weight);
                    }
                }

                score += FeatureScore;
                double perfectFeatureScore =FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count;
                perfectScore += perfectFeatureScore;
            }
            foreach (int FeatureID in FeatureFreqs2.Keys)
            {
                double perfectFeatureScore = FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count;
                double FeatureScore = 0;
                if (FeatureFreqs1.ContainsKey(FeatureID))
                {
                    // do nothing, already added
                }
                else if (useNGramSimilarity && ngramLen>0)
                {
                    Dictionary<int, double> similarFeatures = featureSimilarityMatrix[FeatureID];
                    foreach (int similarFeatureID in similarFeatures.Keys)
                    {
                        if (FeatureFreqs1.ContainsKey(similarFeatureID) &&
                            FeatureFreqs1[similarFeatureID].Count * FeatureFreqs1[similarFeatureID].Weight > FeatureScore)
                            FeatureScore = (FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count +
                                         FeatureFreqs1[similarFeatureID].Count * FeatureFreqs1[similarFeatureID].Weight);
                    }
                }
                perfectScore += perfectFeatureScore;
            }

            return (double)(perfectScore - score) / perfectScore;
        }

        public double CalculateDistance(
            Dictionary<int, FeatureFreq> FeatureFreqs1,
            Dictionary<int, FeatureFreq> FeatureFreqs2,
            Dictionary<int, double> FeatureWeights, Dictionary<int, string> FeatureStemmings1,
            Dictionary<string, List<int>> FeatureStemmings2,
            ref Dictionary<int, int> contributingFeatures)
        {
            if (FeatureFreqs1 == null || FeatureFreqs1.Count == 0 ||
                FeatureFreqs2 == null || FeatureFreqs2.Count == 0)
                return 0;

            double score = 0;
            double perfectScore = 0;

            foreach (int FeatureID in FeatureFreqs1.Keys)
            {
                double FeatureScore = 0;
                if (FeatureFreqs2.ContainsKey(FeatureID))
                {
                    FeatureScore =
                        (FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count +
                        FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count);

                    contributingFeatures.Add(
                        FeatureID,
                        Math.Min(FeatureFreqs1[FeatureID].Count, FeatureFreqs2[FeatureID].Count));
                }
                else 
                {
                    List<int> sharedFeatureIDsAll = FeatureStemmings2[FeatureStemmings1[FeatureID]];
                    foreach (int similarFeatureID in sharedFeatureIDsAll)
                    {
                        if (FeatureFreqs2.ContainsKey(similarFeatureID) &&
                            FeatureFreqs2[similarFeatureID].Count * FeatureFreqs2[similarFeatureID].Weight > FeatureScore)
                            FeatureScore = (FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count +
                                         FeatureFreqs2[similarFeatureID].Count * FeatureFreqs2[similarFeatureID].Weight);
                    }
                }

                score += FeatureScore;
                double perfectFeatureScore = FeatureFreqs1[FeatureID].Weight * FeatureFreqs1[FeatureID].Count;
                perfectScore += perfectFeatureScore;
            }
            foreach (int FeatureID in FeatureFreqs2.Keys)
            {
                double perfectFeatureScore = FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count;
                double FeatureScore = 0;
                if (FeatureFreqs1.ContainsKey(FeatureID))
                {
                    // do nothing, already added
                }
                else 
                {
                    List<int> sharedFeatureIDsAll = FeatureStemmings2[FeatureStemmings1[FeatureID]];
                    foreach (int similarFeatureID in sharedFeatureIDsAll)
                    {
                        if (FeatureFreqs1.ContainsKey(similarFeatureID) &&
                            FeatureFreqs1[similarFeatureID].Count * FeatureFreqs1[similarFeatureID].Weight > FeatureScore)
                            FeatureScore = (FeatureFreqs2[FeatureID].Weight * FeatureFreqs2[FeatureID].Count +
                                         FeatureFreqs1[similarFeatureID].Count * FeatureFreqs1[similarFeatureID].Weight);
                    }
                }
                perfectScore += perfectFeatureScore;
            }

            return (double)(perfectScore - score) / perfectScore;
        }

        #endregion
    }
}
